package dijkstra

import (
	"fmt"
	"strings"

	"dev.ixmxm.top/rms/model"
)

var ErrNoPath = fmt.Errorf("no path")

type edge struct {
	node   string
	weight func(sn string) float64
}
type pathInfo struct {
	PathBlocks   model.PathLocks            //路径锁
	SourceBlocks map[string]model.PathLocks //起点锁
	PathPolygon  model.PoseArray            //路径多边形
}

func (e edge) String() string {
	return fmt.Sprintf("%s(%f)", e.node, e.weight(""))
}

type Graph struct {
	pathList map[string]pathInfo
	edges    map[string][]edge
}

func NewGraph() *Graph {
	return &Graph{edges: make(map[string][]edge), pathList: make(map[string]pathInfo, 0)}
}

func (g *Graph) DeepCopy() *Graph {
	gCopy := NewGraph()
	//log.Info(" =========== raw =========== ")
	//for k, _ := range g.edges {
	//	if strings.Contains(k, ":10021") {
	//		log.Info(g.edges[k])
	//	}
	//}
	//log.Info(" =========== === =========== ")
	for k, v := range g.edges {
		res := make([]edge, len(v), cap(v))
		copy(res, v)
		gCopy.edges[k] = res
	}
	return gCopy
}
func (g *Graph) AddEdgeAndInfo(origin, destiny, start, index string, weight func(sn string) float64, p *model.TrafficPath) {
	if _, find := g.pathList[index]; find {
		g.pathList[index].SourceBlocks[start] = p.SourceBlocks
	} else {
		sourceInfo := make(map[string]model.PathLocks, 0)
		sourceInfo[start] = p.SourceBlocks
		g.pathList[index] = pathInfo{
			PathBlocks:   p.PathBlocks,
			SourceBlocks: sourceInfo,
			PathPolygon:  p.PathPolygon,
		}
	}

	g.edges[origin] = append(g.edges[origin], edge{node: destiny, weight: weight})
}
func (g *Graph) AddEdge(origin, destiny string, weight func(sn string) float64) {
	//matchOrigin := true//strings.Contains(origin, "300052-10090-300051:10090")
	//matchDes := strings.Contains(destiny, "300051-10130:10130")
	//if matchOrigin && matchDes {
	//	fmt.Printf("add edge:%s -> %s,cost:%f\n", origin, destiny, weight)
	//}
	//for _, e := range g.edges[origin] {
	//	if e.node == destiny {
	//		return
	//	}
	//}
	g.edges[origin] = append(g.edges[origin], edge{node: destiny, weight: weight})
}

func (g *Graph) RemoveVertex(origin string) {
	for key, _ := range g.edges {
		if strings.Contains(key, ":"+origin) {
			delete(g.edges, key)
			continue
		}
		for i := 0; i < len(g.edges[key]); {
			if strings.Contains(g.edges[key][i].node, ":"+origin) {
				g.edges[key] = append(g.edges[key][:i], g.edges[key][i+1:]...)
			} else {
				i++
			}
		}
	}
}

func (g *Graph) RemoveEdge(origin, destiny string) {
	for originKey, _ := range g.edges {
		if !strings.Contains(originKey, ":"+origin) {
			continue
		}
		for i := 0; i < len(g.edges[originKey]); {
			if strings.Contains(g.edges[originKey][i].node, ":"+destiny) {
				g.edges[originKey] = append(g.edges[originKey][:i], g.edges[originKey][i+1:]...)
			} else {
				i++
			}
		}
	}
}

func (g *Graph) Shortest(sn, origin, destiny string) ([]string, float64, error) {
	// log.Info(" =========== copy =========== ")
	// for k, _ := range g.edges {
	// 	if strings.Contains(k, ":17390") {
	// 		log.Info(g.edges[k])
	// 	}
	// }
	// log.Info(" =========== --- =========== ")
	// for key, _ := range g.edges {
	// 	if strings.Contains(key, fmt.Sprintf(":%s", "17391")) {
	// 		fmt.Println(g.edges[key])
	// 	}
	// 	for i := 0; i < len(g.edges[key]); i++ {
	// 		if strings.Contains(g.edges[key][i].node, fmt.Sprintf(":%s", "17391")) {
	// 			fmt.Println(g.edges[key][i].node)
	// 		}
	// 	}
	// }
	h := newHeap()
	h.push(path{value: 0, nodes: []string{origin}})
	visited := make(map[string]bool)

	for len(*h.values) > 0 {
		// Find the nearest yet to visit node
		p := h.pop()
		node := p.nodes[len(p.nodes)-1]

		if visited[node] {
			continue
		}

		if node == destiny {
			return p.nodes, p.value, nil
		}

		for _, e := range g.getEdges(node) {
			if !visited[e.node] {
				// We calculate the total spent so far plus the cost and the path of getting here
				h.push(path{value: p.value + e.weight(sn), nodes: append([]string{}, append(p.nodes, e.node)...)})
			}
		}

		visited[node] = true
	}

	return nil, 0.0, ErrNoPath
}

func (g *Graph) getEdges(node string) []edge {
	return g.edges[node]
}
